//
//
//
//
// Socket.C
//

#include "Socket.h"
#include "StrStream.h"
#include "Exception.h"
#include <iostream>
Socket::Socket() 
{
	_sockHdl = new SocketArea();
	_sockHdl->_fileID = -1;
	_sockHdl->_valid = false;

	// Socket area is invalide
	_sockHdl->_refCnt = 1;
}

Socket::Socket(S32 fileID) 
{
	_sockHdl = new SocketArea();
	_sockHdl->_fileID = fileID;
	_sockHdl->_valid = fileID == -1 ? false : true;
	_sockHdl->_refCnt = 1;
}

Socket::Socket(const Socket& other)
{
	other._sockHdl->_refCnt++;
	this->_sockHdl = other._sockHdl;
}

Socket::~Socket()
{
	_sockHdl->_refCnt--;
	if(_sockHdl->_refCnt == 0 && _sockHdl->_fileID != -1)
	{
		close(_sockHdl->_fileID);
		_sockHdl->_valid = false;
	}
}

Socket& Socket::operator=(const Socket& other)
{
	if(this == &other)
		return *this;
	other._sockHdl->_refCnt++;
	_sockHdl = other._sockHdl;
	return *this;
}

S32 Socket::FileID(void) const
{
	return _sockHdl->_fileID;
}

// Write data to this socket
// throw exception if the folloing :
// 1, socket invalid
// 2, write error
//
void Socket::Write(const char* buf, U32 size)
{
	if(NULL == buf)
	{
		Util::StrStream str("NULL input when call Socket::Write");
		throw Exception(str);
	}

	if(!_sockHdl->_valid)
	{
		Util::StrStream str;
		str << "Socket invalid : "
			<< _sockHdl->_fileID;
		throw Exception(str);
	}
	
	S32 _fileID = _sockHdl->_fileID;
	S32 ret = 0;
	U32 left = size;
	while(left)
	{
		ret = write(_fileID, buf + (size - left), left);
		if(-1 == ret)
		{
			Util::StrStream str("Error write socket");
			str << "file id : " << _fileID
				<< ", " << strerror(errno);
			throw Exception(str);
		}
		left -= ret;
	}
}

U32 Socket::Read(void* buf, U32 size)
{
	if(NULL == buf)
		throw Exception("NULL parameter when call Socket::Read");
	S32 _fileID = _sockHdl->_fileID;
	S32 ret = read(_fileID, buf, size);
	if(ret == -1)
	{
		Util::StrStream str;
		str << "Error read socket :"
			<< _fileID
			<< ", " << strerror(errno);
		throw Exception(str.AsString());
	}
	return ret;
}
//
//
void Socket::Shutdown(Socket::SHUTDOWN_OPT opt)
{
	S32 _fileID = _sockHdl->_fileID;
	if(-1 == shutdown(_fileID, opt))
	{
		Util::StrStream str;
		str << "Error Shutdown socket: "
			<<_fileID
			<< ", "
			<< strerror(errno);
			throw Exception(str.Str());
	}
}

//
// get socket handle
SocketHdl Socket::GetSockHdl(void)
{
	return _sockHdl;
}

bool Socket::operator==(const Socket& other)
{
	return _sockHdl->_fileID == other._sockHdl->_fileID;
}
